
#include "ActionHttpProxy.h"
#include "ActionHttpBase.h"
#include "context.h"
#include "1700_login.pb.h"
#include "9100_agency_api.pb.h"
#include "minutelisten.h"
#include "util.h"
#include "log.h"
#include "NodeCmd.h"



#define EXPIRE_HTTP_REQ_MS 10000  // http请求超时10秒

template <typename ResponseType>
void ErrorDefaultHttpResponse(std::uint32_t request_id, std::uint64_t request_time, std::int32_t errorCode, const std::string& message, const HttpSessionData& http_session)
{
    ResponseType resp;
    resp.set_errorcode(errorCode);
    resp.set_message(message);

    ResponseHttp(request_id, request_time, http_session, &resp);
}

template <>
void ErrorDefaultHttpResponse<account_proto::VerifyLoginTokenResp>(std::uint32_t request_id, std::uint64_t request_time, std::int32_t errorCode, const std::string& message, const HttpSessionData& http_session)
{
    account_proto::VerifyLoginTokenResp resp;
    resp.set_result(errorCode);

    ResponseHttp(request_id, request_time, http_session, &resp);
}



static bool __Init__ = ActionHttpProxy::Instance()->Init();

ActionHttpProxy::ActionHttpProxy()
{
}

ActionHttpProxy::~ActionHttpProxy()
{
}

bool ActionHttpProxy::Init()
{
    m_trans_id = 0;

    using namespace std::placeholders;
    using respCallFunc = void(*)(std::uint32_t request_id, std::uint64_t request_time, std::int32_t errorCode, const std::string& message, const HttpSessionData& http_session);

    // SERVER_TYPE_ACCOUNT
    RegiserHttpRoute("post", "/auth", SERVER_TYPE_ACCOUNT, account_proto::ACCOUNT_CMD_VERIFY_LOGIN_TOKEN_REQ, account_proto::ACCOUNT_CMD_VERIFY_LOGIN_TOKEN_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<account_proto::VerifyLoginTokenResp>), _1, _2, _3, _4, _5));

    // SERVER_TYPE_AGENCY_API
    RegiserHttpRoute("post", "/balance", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_GET_BALANCE_REQ, agency_api::AGENCY_API_MSGID_GET_BALANCE_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::BalanceResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/bet", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_BET_REQ, agency_api::AGENCY_API_MSGID_BET_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::BetResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/betwin", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_WIN_REQ, agency_api::AGENCY_API_MSGID_WIN_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::BetAndWinResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/canclebet", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_CANCLE_BET_REQ, agency_api::AGENCY_API_MSGID_CANCLE_BET_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::CancelBetResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/canclebetwin", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_CANCLE_BET_AND_WIN_REQ, agency_api::AGENCY_API_MSGID_BET_AND_WIN_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::CancleBetAndWinResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/reward", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_REWARD_REQ, agency_api::AGENCY_API_MSGID_REWARD_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::RewardResp>), _1, _2, _3, _4, _5));
    RegiserHttpRoute("post", "/kickout", SERVER_TYPE_AGENCY_API, agency_api::AGENCY_API_MSGID_KICK_PLAYER, 0, ALLOC_RANDOM, nullptr);
    RegiserHttpRoute("post", "/win", SERVER_TYPE_AGENCY_API,  agency_api::AGENCY_API_MSGID_WIN_REQ, agency_api::AGENCY_API_MSGID_WIN_RESP, ALLOC_RANDOM,
        std::bind(static_cast<respCallFunc>(ErrorDefaultHttpResponse<agency_api::WinResp>), _1, _2, _3, _4, _5));

    
    MinuteListen::Instance()->Register(__FUNCTION__, [this](std::int64_t tick){
        this->OnTick(tick);
    });
    return true;
}

bool ActionHttpProxy::RegiserHttpRoute(const std::string& method, const std::string& path, uint32_t reqSrvType, uint32_t reqCmd, uint32_t rspCmd, uint32_t modUidType, ERRRESPCALLFUNC errRespCallFunc)
{
    std::string tmpPath = path.substr(1, path.size() - 1);
    std::string key = CTools::ToUpStr(method) + ":" + tmpPath;
    if (m_http_route.find(key) != m_http_route.end())
    {
        log_error("http route exist, method:%s,path:%s", method.c_str(), path.c_str());
        return false;
    }
    ActionHttpRouteData data;
    data.method = CTools::ToUpStr(method);
    data.path = path;
    data.reqCmd = reqCmd;
    data.rspCmd = rspCmd;
    data.reqSrvType = reqSrvType;
    data.modUidType = modUidType;
    data.errRespCallFunc = errRespCallFunc;
    m_http_route[key] = data;

    Ctx.msg_router.BindHttpHandle(data.method, data.path, std::bind(&ActionHttpProxy::HttpHandle, this, std::placeholders::_1));
    if (data.rspCmd != 0)
    {
        Ctx.msg_router.BindMsg(data.rspCmd, std::bind(&ActionHttpProxy::MsgHandle, this, std::placeholders::_1));
    }
    
    return true;
}

void ActionHttpProxy::HttpHandle(HttpData&& data)
{
    std::uint64_t request_time = CTools::GetUnixTimeMs();
    if (data.sub_paths.size() == 0)
    {
        log_error("sub_paths empty, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
        httpErrResponse(data.request_id, request_time, 404, data.http_session, "");
        return;
    }

    std::string key = CTools::ToUpStr(data.method) + ":" + data.sub_paths[data.sub_paths.size() - 1];
    std::map<std::string, ActionHttpRouteData>::const_iterator it = m_http_route.find(key);
    if (it == m_http_route.end())
    {
        log_error("http route not found, key: %s, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", key.c_str(), data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
        httpErrResponse(data.request_id, request_time, 404, data.http_session, "");
        return;
    }

    int32_t errorCode = 0;
    std::string message;
    const ActionHttpRouteData& router = it->second;
    do
    {
        log_debug("request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
        // 先校验请求合法
        if (!httpCheckRequest(data))
        {
            log_error("check sign failed, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
            errorCode = agency_api::ErrorCode::InputParamErr;
            message = "check sign failed";
            break;
        }

        // 发送转发消息
        if (router.modUidType == ALLOC_MOD)
        {
            Ctx.host->MtSendtoSrv(router.reqCmd, router.reqSrvType, std::move(data.body), data.http_session.uid, false, data.request_id);
        }
        else
        {
            Ctx.host->MtSendtoSrv(router.reqCmd, router.reqSrvType, std::move(data.body), 0, false, data.request_id);
        }

        if (router.rspCmd == 0)
        {
            // 不需要响应
            log_debug("NoRsp, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
            httpResponse(data.request_id, request_time, data.http_session, "");
        }
        else
        {
            // 之前的http 消息还未返回， 先清除之前的临时数据
            if (m_httpreq_info_map.find(data.request_id) != m_httpreq_info_map.end())
            {
                log_error("http req info exist, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s, bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
                RemoveHttpReqTempData(data.request_id);
            }

            // 记录请求临时缓存
            std::uint64_t trans_id = ++m_trans_id;
            m_httpreq_info_map[data.request_id].request_id = data.request_id;
            m_httpreq_info_map[data.request_id].http_session = data.http_session;
            m_httpreq_info_map[data.request_id].request_time = request_time;
            m_httpreq_info_map[data.request_id].trans_id = trans_id;

            // 超时检测链表
            m_expire_httpreq_map[trans_id] = data.request_id;

            log_debug("request request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
        }

        return;
    }while(false);

    // 走到这里说明请求失败
    if (router.errRespCallFunc != nullptr)
    {
        router.errRespCallFunc(data.request_id, request_time, errorCode, message, data.http_session);
    }
    else
    {
        log_error("errRespCallFunc null, request_id: %u, uid: %u, tid: %lu, acc: %s, ip: %s, target: %s,bodyLen: %lu", data.request_id, data.http_session.uid, data.http_session.tid, data.http_session.acc.c_str(), data.ip.c_str(), data.target.c_str(), data.body.size());
    }
}

void ActionHttpProxy::MsgHandle(MsgData&& data)
{
    log_debug("enter,stype:%hu,svid:%u,uuid:%u", data.svtype, data.svid, data.uuid);
    std::unordered_map<std::uint32_t, HttpReqTempData>::const_iterator it = m_httpreq_info_map.find(data.uuid);
    if (it == m_httpreq_info_map.end())
    {
        log_error("http req info not found, uuid:%u", data.uuid);
        return;
    }
    
    const HttpReqTempData& tmpData = it->second;
    httpResponse(tmpData.request_id, tmpData.request_time, tmpData.http_session, std::move(data.data));
    log_debug("Response, stype: %hu, svid: %u, request_id: %u, uid: %u, tid: %lu, acc: %s, bodyLen: %lu", data.svtype, data.svid, data.uuid, tmpData.http_session.uid, tmpData.http_session.tid, tmpData.http_session.acc.c_str(), data.data.size());
    
    // 移除http 临时缓存
    RemoveHttpReqTempData(data.uuid);
    return;

}

void ActionHttpProxy::OnTick(std::int64_t tick)
{
    std::uint64_t now = CTools::GetUnixTimeMs();
    for (std::map<std::uint64_t, std::uint32_t>::iterator it = m_expire_httpreq_map.begin(); it != m_expire_httpreq_map.end(); )
    {
        std::uint32_t request_id = it->second;
        std::unordered_map<std::uint32_t, HttpReqTempData>::iterator req_it = m_httpreq_info_map.find(request_id);
        if (req_it == m_httpreq_info_map.end())
        {
            log_error("http req info not found, request_id:%u", request_id);
            it = m_expire_httpreq_map.erase(it);
            continue;
        }
        const HttpReqTempData& tmpData = req_it->second;
        if (now - tmpData.request_time >= EXPIRE_HTTP_REQ_MS)
        {
            m_httpreq_info_map.erase(req_it);
            it = m_expire_httpreq_map.erase(it);
            continue;
        }
        break;
    }
}

void ActionHttpProxy::RemoveHttpReqTempData(std::uint32_t request_id)
{
    std::unordered_map<std::uint32_t, HttpReqTempData>::const_iterator it = m_httpreq_info_map.find(request_id);
    if (it == m_httpreq_info_map.end())
        return;
    
    const HttpReqTempData& tmpData = it->second;
    m_expire_httpreq_map.erase(tmpData.trans_id);
    m_httpreq_info_map.erase(it);
}